Odjel za informatiku, Sveučilište u Rijeci
Akademska godina 2021/2022
Odabrani portal: https://hr.n1info.com/
Autor: Andrea Hrelja
Nositelji kolegija:
Dokumentacija prikupljanja podataka dostupna je u korijenu ovog repozitorija u datoteci README.md. U nastavku je implementiran i dokumentiran proces Analize podataka.
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import pandas as pd
import os
ARTICLES_CLEAN_PATH = 'C:\\Users\\AndreaHrelja\\Documents\\Faks\\5. godina\\3. semestar\\UPZ\\Scrapeinar\\scrape\\n1info\\csv\\clean-n1info.csv'
MONTH_WORDCOUNT_PATH = 'C:\\Users\\AndreaHrelja\\Documents\\Faks\\5. godina\\3. semestar\\UPZ\\Scrapeinar\\scrape\\n1info\\csv\\wordcount-n1info.csv'
df = pd.read_csv(ARTICLES_CLEAN_PATH, encoding='utf-8')
df['tags'] = df['tags'].map(eval)
df['categories'] = df['categories'].map(eval)
df = df[[
'id', 'datetime', 'date', 'month', 'category_name',
'title', 'author', 'text', 'categories', 'tags',
'covid_related', 'vaccine_related', 'anti_related', 'soj_related'
]]
rename_map = {
'covid_related': "Objave vezane uz korona tematiku",
'vaccine_related': "Objave vezane uz tematiku cijepljenja",
'anti_related': "Objave vezane uz tematiku antimaskera/antivaksera",
'soj_related': "Objave vezane uz tematiku sojeva",
'total': "Ukupno objava"
}
df.info()
U sklopu analize koriste se dvije CSV datoteke:
| Naziv kolumne | Tip podatka | Opis podatka |
|---|---|---|
| id | int | Jedinstveni identifikator članka |
| datetime | datetime | Datum i vrijeme (YYYY-MM-DD HH:MM:SS) članka |
| date | date | Datum (YYYY-MM-DD) članka |
| month | date | Datum (YYYY-MM-01) članka |
| category_name | string | Naziv kategorije glavne članka |
| title | string | Naslov članka |
| author | string | Autor članka |
| text | string | Tekst članka |
| categories | string (list) | Kategorije članka (Python lista u obliku tekstualnog niza) |
| tags | string (list) | Tagovi članka (Python lista u obliku tekstualnog niza) |
| covid_related | boolean | Članak je vezan uz COVID tematiku |
| vaccine_related | boolean | Članak je vezan uz tematiku cijepljenja |
| anti_related | boolean | Članak je vezan uz tematiku antimaskera/antivaksera |
| soj_related | boolean | Članak je vezan uz tematiku novih sojeva |
| Naziv kolumne | Tip podatka | Opis podatka |
|---|---|---|
| month | date | Datum (YYYY-MM-01) članka |
| title | string | Konkatenacija naslova i teksta članka |
fig, ax = plt.subplots(1, figsize=(8, 6))
category_df = df.groupby('category_name').size() \
.reset_index(name='counts') \
.sort_values('counts', ascending=False) \
.head(6)
most_common_categories = category_df['category_name'].unique()
category_df.plot(x='category_name', kind='bar', rot=0, ax=ax)
ax.set_title('Broj objava po kategorijama')
ax.set_xlabel('\nKategorija')
plt.show()
num_anti_related = len(df[df['anti_related'] == True])
num_soj_related = len(df[df['soj_related'] == True])
num_vaccine_related = len(df[df['vaccine_related'] == True])
num_covid_related = len(df[df['covid_related'] == True])
print('a) Ukupan broj objava na portalu za vremenski period 1.1.2021. - 31.12.2021.: {:,.0f}'.format(len(df)))
print('b) Broj vijesti vezanih za korona tematiku: {:,.0f}'.format(num_covid_related))
print('c) Broj vijesti vezanih za cijepljenje: {:,.0f}'.format(num_vaccine_related))
print('d) Broj vijesti vezanih za antimaskere/antivaksere: {:,.0f}'.format(num_anti_related))
print('*) Broj vijesti vezanih za nove sojeve: {:,.0f}'.format(num_soj_related))
pd.DataFrame(
{
'count': [
num_anti_related,
num_soj_related,
num_vaccine_related,
num_covid_related,
]
}, index=[
'Broj vijesti vezanih za antimaskere/antivaksere',
'Broj vijesti vezanih za nove sojeve',
'Broj vijesti vezanih za cijepljenje',
'Broj vijesti vezanih za korona tematiku'
]
).plot.barh(legend=None, figsize=(8, 6))
plt.show()
day_df = df[['date', 'covid_related', 'anti_related', 'vaccine_related']].groupby('date').sum()
day_df['total'] = df.groupby('date').size()
day_df = day_df[['total', 'covid_related', 'vaccine_related', 'anti_related']]
day_df.rename(rename_map, axis=1)
month_df = df[['month', 'covid_related', 'anti_related', 'vaccine_related']].groupby('month').sum()
month_df['total'] = df.groupby('month').size()
month_df = month_df[['total', 'covid_related', 'vaccine_related', 'anti_related']].rename(rename_map, axis=1)
month_df.plot(kind='bar', rot=0, figsize=(16, 8))
plt.show()
month_df.rename(rename_map, axis=1)
fig, ax = plt.subplots(nrows=2, figsize=(14, 14))
df[df['covid_related'] == True].groupby('date').size() \
.reset_index(name='counts') \
.sort_values('date') \
.set_index('date') \
.plot(ax=ax[0], legend=None)
df.groupby('date').size() \
.reset_index(name='counts') \
.sort_values('date') \
.set_index('date') \
.plot(ax=ax[0], legend=None)
df[df['covid_related'] == True].groupby('month').size() \
.reset_index(name='counts') \
.sort_values('month') \
.set_index('month') \
.plot(ax=ax[1], legend=None)
df.groupby('month').size() \
.reset_index(name='counts') \
.sort_values('month') \
.set_index('month') \
.plot(ax=ax[1], legend=None)
ax[0].set_title('Broj objava vezanih za korona tematiku po datumu')
ax[0].set_xlabel('\nDatum')
ax[0].legend([rename_map[name] for name in ['covid_related', 'total']])
ax[1].set_title('Broj objava vezanih za korona tematiku po mjesecu')
ax[1].set_xlabel('\nDatum')
ax[1].legend([rename_map[name] for name in ['covid_related', 'total']])
plt.show()
Slijedeći grafikon (Word Cloud) prikazuje ukupnu frekvenciju pojavljivanja tagova u člancima za period od 1.1.2021 - 31.12.2021.
cattags_df = df[['date', 'month', 'category_name', 'tags']].dropna() \
.explode('tags') \
.reset_index(drop=True) \
.set_index('category_name')
tag_frequency = cattags_df.groupby('tags').size() \
.reset_index(name='counts') \
.sort_values('counts', ascending=False) \
.set_index('tags')
fig, ax = plt.subplots(figsize=(14, 7))
wordcloud = WordCloud(width=800, height=500, background_color='white').generate_from_frequencies(tag_frequency.to_dict()['counts'])
ax.imshow(wordcloud)
ax.axis('off')
ax.set_title('Ukupna frekvencija pojavljivanja tagova u člancima')
plt.show()
Slijedeći grafikon (Word Cloud) prikazuje frekvenciju pojavljivanja tagova u člancima po pojedinoj kategoriji za period od 1.1.2021 - 31.12.2021.
counts_df = cattags_df.groupby(['category_name', 'tags']).size() \
.reset_index(name='counts') \
.sort_values('counts', ascending=False) \
.set_index('category_name')
print("Frekvencija pojavljivanja tagova za pojedinu kategoriju")
ncols = 2
nrows = len(most_common_categories) / ncols
fig, axs = plt.subplots(nrows=int(nrows), ncols=ncols, figsize=(nrows*5, ncols*7))
for i, category_name in enumerate(most_common_categories):
frequencies = {
item['tags']: item['counts']
for item in counts_df.loc[category_name].to_dict(orient='records')
}
wordcloud = WordCloud(background_color='white').generate_from_frequencies(frequencies)
axs[i // 2, i % 2].set_title("Kategorija: " + category_name)
axs[i // 2, i % 2].imshow(wordcloud, interpolation='bilinear')
axs[i // 2, i % 2].axis('off')
Slijedeći grafikon (Word Cloud) prikazuje frekvenciju pojavljivanja top 25 tagova u člancima po mjesecu za period od 1.1.2021 - 31.12.2021. S desne je strane "bar" graf koji prikazuje frekvenciju pojavljivanja top 10 tagova u člancima po mjesecu.
monthtags_df = cattags_df.groupby(['month', 'tags']).size() \
.reset_index(name='counts') \
.sort_values('counts', ascending=False) \
.set_index('month')
print("Frekvencija pojavljivanja tagova po mjesecu")
ncols = 2
nrows = len(monthtags_df.index.unique())
fig, axs = plt.subplots(nrows=int(nrows), ncols=ncols, figsize=(ncols*8, nrows*9))
for i, month in enumerate(sorted(monthtags_df.index.unique())):
month_items = monthtags_df.loc[month]
frequencies = {
item['tags']: item['counts']
for item in month_items.to_dict(orient='records')[:25]
}
wordcloud = WordCloud(background_color='white', height=400, width=600).generate_from_frequencies(frequencies)
axs[i, 0].set_title("Datum: " + str(month)[:7])
axs[i, 0].imshow(wordcloud, interpolation='bilinear')
axs[i, 0].axis('off')
month_items.head(10).sort_values('counts').plot.barh(x='tags', ax=axs[i, 1])
axs[i, 1].set_ylabel(None)
Primjećuje se da je u svakom mjesecu tag "koronavirus" najčešći, osim u svibnju (2021-05) kada je tag "lokalni izbori" najzastupljenija vijest.
U nastavku slijedi tablični prikaz unigrama za top 25 tagova u člancima po mjesecu za period od 1.1.2021 - 31.12.2021.
for i, month in enumerate(sorted(monthtags_df.index.unique())):
print("Ispis unigrama za {}".format(str(month)[:7]))
print(monthtags_df.loc[month][:25].reset_index(drop=True))
print()
Slijedeći blok definira dijakritičke znakove, znakove koji će se izbrisati iz ukupnog zbira tekstova i naslova članaka (replace_chars) te zaustavne riječi koje su odabrane ručnim pregledom unigrama iz ukupnog zbira tekstova i naslova članaka.
diacritics = {
'č': 'c',
'ć': 'c',
'š': 's',
'ž': 'z'
}
replace_chars = [
'”','\t','\n','!','#','$','%','&','/','(',')','[',']','{','}','=',
'?','*','\\','|','€','÷','×',',','.','_','@',':','"',';',':','\xa0'
]
stop_words = [
'protiv', 'hrvatskoj', 'ljudi', 'dana', 'novi', 'novih', 'posto', 'komentar', 'nove', 'evo', 'traži', 'biste', 'broj',
'je','u','i','da','se','na','za','su','od','s','će','a','koji','o','to','ne','što','kako','bi','putem','te','iz','do',
'nije','koje','biti','rekao','kao','ali','ili','koja','zbog','sve','smo','može','–','po','jer','sa','još','ako','bilo',
'li','oko','ima','prema','bio','sam','dok','kada','mogu','pa','prije','već','nego','nisu','kod','uz','treba','ih','mi',
'osoba','sada','tako','bez','tri','no','ga','neće','kaže','nema','rekla','toga','dva','ćemo','bila','danas','jedan',
'gdje','kojima','kazao','kad','između','tome','godina','oni','ni','imaju','ove','tijekom','među','taj','tu','on',
'nekoliko','koju','radi','dvije','pitanje','svoje','vrijeme','svi','dio','im','dalje','bih','nešto','osobe','prvi',
'je','jedna','bili','također','njih','mu','odnosno','vrlo','ovo','uvijek','način','četiri','n1','tom','dodao','mora',
'kojoj','druge','imamo','onda','24','koliko','ponedjeljak','ja','nas','kojem','godine','neki','poput','pet','smatra',
'odnosu','pod','navodi','dan','ono','neke','tko','preko','kroz','čak','kuna','drugi','piše','one','sata','utorak',
'srijedu','ta','istaknuo','suda','svoj','ovaj','mislim','ona','malo','niti','tim','petak','jednom','ovom','četvrtak',
'sad','kojih','bude','svim','pri','vam','bit','tek','šest','čega','mogao','možete','svojim','sati','svoju','znači',
'mogli','tjedna','riječ','obzirom','osim','zato','zašto','ste','iako','tog','žele','čemu','time','stvari','naše',
'njima','toj','tada','komentirao','kojeg','kazala','doći','tiče','mogla','tih','nad','isto','imao','drugom','jednu',
'svom','bismo','se','ipak','bile','ova','n','samo','nam','imati','moći', 'nakon','više','dosad','milijuna'
]
stop_words_no_diacritics = []
for stop_word in stop_words:
if any(key in stop_word for key in diacritics.keys()):
for key, value in diacritics.items():
stop_word = stop_word.replace(key, value)
stop_words_no_diacritics.append(stop_word)
def get_word_count(text):
word_count = {}
for char in replace_chars:
text = text.replace(char, ' ')
all_text = text.lower().split(" ")
for word in all_text:
if word and not word.isnumeric() and word not in stop_words and word not in stop_words_no_diacritics:
try:
word_count[word] += 1
except KeyError:
word_count[word] = 1
sorted_word_count = {k: v for k, v in sorted(word_count.items(), key=lambda x: x[1], reverse=True)[:25]}
return sorted_word_count
Slijedeći blok konkatenira sve naslove i tekstove na razini mjeseca u ukupan zbir naslova i tekstova članaka. Zbog dugog izvođenja agregacije, sadržaj je spremljen u wordcount-n1info.csv.
if not os.path.isfile(MONTH_WORDCOUNT_PATH):
month_titletext_df = df[['month', 'title', 'text']].groupby('month').sum()
month_titletext_df.to_csv(MONTH_WORDCOUNT_PATH, encoding='utf-8')
else:
month_titletext_df = pd.read_csv(MONTH_WORDCOUNT_PATH, encoding='utf-8', index_col='month')
U nastavku slijedi Word Cloud prikaz top 25 najčešćih pojavljivanja riječi ukupnom zbiru naslova i tekstova članaka po mjesecu za period od 1.1.2021 - 31.12.2021. S desne je strane "bar" graf koji prikazuje frekvenciju pojavljivanja top 10 riječi u člancima po mjesecu.
print("Najčešće riječi po mjesecu")
ncols = 2
nrows = len(month_titletext_df.index.unique())
fig, axs = plt.subplots(nrows=int(nrows), ncols=ncols, figsize=(ncols*8, nrows*9))
for i, month in enumerate(sorted(month_titletext_df.index.unique())):
month_items = month_titletext_df.loc[month]
frequencies = get_word_count(month_items['title'])
wordcloud = WordCloud(background_color='white', height=400, width=600).generate_from_frequencies(frequencies)
axs[i, 0].set_title("Datum: " + str(month)[:7])
axs[i, 0].imshow(wordcloud, interpolation='bilinear')
axs[i, 0].axis('off')
pd.DataFrame(frequencies.values(), frequencies.keys(), columns=['count']) \
.head(10) \
.sort_values('count') \
.plot.barh(ax=axs[i, 1])
axs[i, 1].set_ylabel(None)
Slijedeći blok izračunava i prikazuje Jaccardov indeks sličnosti za svaka dva mjeseca u razdoblju od 1.1.2021 do 31.12.2021. Svaki mjesec na x-osi predstavlja Jaccardov indeks sličnosti između ispisanog i prethodnog mjeseca.
def jaccard_similarity(list1, list2):
s1 = set(list1)
s2 = set(list2)
return float(len(s1.intersection(s2)) / len(s1.union(s2)))
index = month_titletext_df.index[1:]
data = {'jaccard_similarity': []}
for i, month in enumerate(month_titletext_df.index):
if i == 0:
continue
prev_month = month_titletext_df.index[i-1]
curr_month = month_titletext_df.index[i]
prev_month_text = month_titletext_df.loc[prev_month]['title']
curr_month_text = month_titletext_df.loc[curr_month]['title']
prev_word_count = get_word_count(prev_month_text)
curr_word_count = get_word_count(curr_month_text)
data['jaccard_similarity'].append(jaccard_similarity(prev_word_count.keys(), curr_word_count.keys()))
pd.DataFrame(data=data, index=index).plot(kind='bar', rot=30, figsize=(14, 8))
plt.show()